<?php
/**################################################################################################**   
# Copyright (c)  2008  Carlos Galindo.   
# Permission is granted to copy, distribute and/or modify this document   
# under the terms of the GNU Free Documentation License, Version 1.2   
# or any later version published by the Free Software Foundation;   
# Provided 'as is' with no warranties, nor shall the autor be responsible for any mis-use of the same.     
# A copy of the license is included in the section entitled 'GNU Free Documentation License'.   
#   
#   CJAX FRAMEWORK 3.0.0RC2                $     
#   ajax made easy with cjax                    
#   -- DO NOT REMOVE THIS --                    
#   -- AUTHOR COPYRIGHT MUST REMAIN INTACT -   
#   CJAX FRAMEWORK  3.0.0RC2               $     
#   Written by: Carlos Galindo                  
#   Website: www.cjax.net                     $      
#   Email: cjxxi@msn.com    
#   Date: 2/12/2007                           $     
#   File Last Changed:  03/24/2009            $     
**####################################################################################################*/   

/**
 *  @package CoreEvents
 */

/**
 * Load external classes
 *
 * @param string $c
 */
class CoreEvents extends cjaxFormat {
	
	//public static $action_cache = array();
	
	public  static $cjax_data_counter = 0;
	
	/**
	 * default port when connecting to web pages
	 *
	 * @var unknown_type
	 */
	public $port = 80;
	
	/**
	 * if controllers are located in a sub directory
	 *
	 * @var string
	 */
	public $controller_dir = '';
	/*
	 * hold an object with some formattig helpers
	 * not meant to be added to the package but it was added at some point
	 * @return cjaxFormat
	 */
	public $format;
	
	/**
	 * Where the data is stored before the output
	 *
	 * @var unknown_type
	 */
	private static $out = array();
	
	/**
	 * Check weather or not to call the shutdown function
	 *
	 * @var boolean $_is_shutdown_called
	 */
	private static $_is_shutdown_called = false;
	
	/**
	 * store cache procedure
	 *
	 * @var string $cache
	 */
	public static $cache = array();
	
	/**
	 * hold cache for actions
	 *
	 * @var array
	 */
	public static $actions = array();
	
	/**
	 * specified weather to use the cache system or normal mode
	 *
	 * @var boolean $use_cache
	 */
	static $use_cache;
	
	/**
	 * Event which is used for elements to create calls to the server
	 * 
	 *
	 * @var string $JSevent = "onClick";
	 */
	public $JSevent = "onClick";
	
	/**
	 * Set the text to show when the page is loading
	 * this replaces the "loading.."
	 *
	 * @var string $text
	 */
	public $text;
	/**
	 * Set the image that shows up when the page is loading
	 * this replaces teh default image
	 *
	 * @var unknown_type
	 */
	public $image;
	/*
	 * This must be set to true before making a CJAX call,
	 * only if the element you are interacting with is an anchor
	 * or an image
	 * 
	 */
	public $link;
	
	/*
	 * The the CJAX console on debug mode
	 */
	public $debug;
	
	/**
	 * Set the default directory when images loading images reside
	 *
	 * @var string $image_dir
	 */
	public $image_dir;
	
	/**
	 * Get the current version of CJAX FRAMEWORK you are using
	 *
	 * @var string
	 */
	public $version;
	
	/**
	 * Define that you are using an external extension
	 * 
	 *
	 * @var string
	 */
	public $extension;
	
	/**
	 * When using th plugin system, you will need to specify what the pluing base url is
	 * 
	 */
	private $extension_dir;
	
	/**
	 * Tells whether CJAX output has initciated or not, to void duplications
	 *
	 * @var boolean $is_init
	 */
	public $is_init;
	
	/**
	 * Sets the default way of making AJAX calls, it can be either get or post
	 */
	public $method ='get';
	/**
	 * Stores the the waiting procedure for the next action
	 */
	private static $wait;
	
	/**
	 * Path where JavaScript Library is located
	 *
	 * @var string
	 */
	public static $path;
	
	/**
	 * Path where JavaScript Library is located
	 *
	 * @var string
	 */
	private $jsdir = null;	
	
	
	public $extension_sub = false;
	/**
	 * Auto execute methods for extensions
	 *
	 * @param string $method
	 * @param array $args
	 */
	
	public $caller;
	
	private static $cache_type;
	
	/**
	 * get a formated date string
	 *
	 * @param string $time_stamp
	 * @param string $type
	 * @return string
	 */
    function date($time_stamp,$type=null)
    {
    	if(!$type  || !is_numeric($time_stamp)) {
    		return;
    	}
    	
    	switch($type) {
    		case 'list':
    			return date("M j y",$time_stamp);
    			break;
    		case 'detail':
    			return date("D, M j Y\n g:i:s A  O",$time_stamp);
    			break;
    		case 'full':
    			return date("D, M j Y\n g:i:s",$time_stamp);
    			break;
    		case 'seconds':
    			$d = date("s",$time_stamp);
    			if($d < 1000) {
					 $d = "0.$d";
				} 
				RETURN $d;
    		break;
    	}
    }
    
	public function __call($method, $args) 
	{
	 	if(!$this->extension) $this->extension = 'plugins';
	 	if(!$this->extension_dir) $this->extension_dir = '__base__/extensions/';
 		extension::add($method,$this->extension_dir,$this->extension);
 		$route = $this->extension_sub? '1':'0';
 		$params = null;
 		$this->extensions[$this->extension][] = array(
 		'method' =>$method,
 		'directory' =>  $this->extension_dir,
 		);
 		$this->caller['function'] = $method;
		$this->caller['extension'] = $this->extension;
 		$fmethod = "extension_{$this->extension}_$method";
 		$testunit1 = CJAX_DIR.DIRECTORY_SEPARATOR.$this->extension.DIRECTORY_SEPARATOR.$method.".php";
 		$testunit2 = CJAX_DIR.DIRECTORY_SEPARATOR.$this->extension.DIRECTORY_SEPARATOR.$method.DIRECTORY_SEPARATOR."$method.php";
 		$this->caller['testunit1'] = $testunit1;
 		$this->caller['testunit2'] = $testunit2;
 		if(file_exists($testunit1)) {
	 		require_once $testunit1;
	 		$this->caller['unit_used'] = 'testunit1';
	 	} else if (file_exists($testunit2)) {
	 		require_once $testunit2;
	 		$this->caller['unit_used'] = 'testunit2';
	 	}
	 	if(class_exists($fmethod)) {
	 		$this->caller['method'] = $fmethod;
	 		$handle = singleton::getInstance($fmethod);
		 	if($handle->html) {
	 			return $handle->html;
	 		}
	 	}
	 	if( count($args) > 0 ) {
	 		$i = 0;
		 	foreach( $args as $arg ){
		 		$i++;
		 		$params[] = "<param$i>{$this->encode($arg)}</param$i>";
		 		$param[] = "<param>{$this->encode($arg)}</param>";
		 	}
		 	$params = "<params>".implode($params)."<array>".implode($param)."</array></params>";
	 	}
		$this->xml("<do>$method</do><extension>$this->extension</extension><ctype>extension_system</ctype><settings><route>$route</route><method>$method</method>{$params}<base>$this->extension_dir</base></settings>");
		
		$this->caller = null;
	}
	
	/**
	 * Setting up the directory where the CJAX FRAMEWORK resides
	 *
	 * @param string $jsdir
	 */
	function js($jsdir,$force=false)
	{
		if($force) {
			self::$path = $jsdir;
			return $this->jsdir = false;
		}
		if(!$this->jsdir && $this->jsdir !==false) {
			$this->jsdir = $jsdir;
		}
	}
	
	/**
	 * Outputs our FRAMEWORK to the browser
	 * @param unknown_type $js_path
	 * @return unknown
	 */
	function head_ref($js_path = null)
	{
		if($this->jsdir) {
			return "<script id='cjax_lib' type='text/javascript' src='{$js_path}cjax.js'></script>\n";
		}
		if(isset(self::$path) && strlen(self::$path) > 0) {
			if(self::$path[strlen(self::$path)-1] =='/') {
				self::$path = substr(self::$path,0,strlen(self::$path) -1);
			}
			return "<script id='cjax_lib' type='text/javascript' src='".self::$path."/core/js/cjax.js'></script>\n";
		}
		
	}

	/**
	 * initciates the process of sending the javascript file to the application
	 *	
	 * @param optional boolean $echo
	 * @return string
	 */
	function init($echo = false)
	{
		$CJAX = CJAX::getInstance();
		self::clearCache();
		if($CJAX->is_init) {
			return;
		}
		if ($echo) {
			echo $this->head_ref ($this->jsdir);
			$this->is_init = true;
			return;
		} else {
			$this->is_init = true;
			return $this->head_ref ($this->jsdir);
		}
	}
		
	/**
	 *  Tell weather of not the a ajax request has been placed
	 *
	 * Sunday August 3 2008 added functionality:
	 * 
	 * 
	 * 
	 * 
	 * @return boolean
	 */
	 function request($callback = null, &$params = null)
	 {
	 	$r = self::get('cjax')? 1 : 0;
	 	if($r && $callback !==null) {
	 		if(is_array($callback) ) {
	 			if(substr($callback[0],0,4)=='self') {
	 				$arr = debug_backtrace();
		 			$trace = $arr[1];
		 			$class = $trace['class'];
	 				$class = $class;
	 				$callback[0] =$class;
	 			}
	 			if(!$params) $params = array();
	 			$r = call_user_func_array($callback,$params);
	 		} else {
	 			$r = call_user_func($callback);
	 		}
	 		exit();
	 	}
	 	return $r;
	 }
	 
	/*
	 * sets up the default loading image
	 */
	function __construct()
	{
		$this->image = "cjax/core/images/loading.gif";
		$this->JSevent();
		$this->format = cjaxFormat::getInstance();
		//extension::init();
	}
	
	/**
	 * Encode special data to void conflicts with javascript
	 *
	 * @param string $data
	 * @return encoded string
	 */
	function encode($data)
	{
		if(!$data) {
			return;
		}
		$data = str_replace(array("\n","\r","\"","\\","\t",";"),array("<nl>","<rt>","<qt>","<bs>","<tb>","<ed>"),$data);
	//	$data = str_replace($search,$replace,$data); 
	//	$data = urlencode($data);
	//	$data = str_replace("+","~S~",$data); 
		return $data;
	}
		
	/**
	 * Not implemented, just for testing purpose
	 *
	 * @param string $xml
	 */
	function isXML($xml)
	{
		if(!is_array($xml)) {
			if(strpos($xml,'<') !==false && strpos($xml,'/>')!==false) { 
				return true;
			}
			return false;
		} else {
			foreach($xml as $k =>$v) {
				if($this->isXML($v)) {
					return true;
				}
			}
		}
	}
	
	/**
	 * Not implemented, just for testing purpose
	 *
	 * @param string $xml
	 */
	function parseXml($xml)
	{
		$this->parseXml('<TAG1>test1</TAG1><TAG2>test2</TAG2><TAG3>test3</TAG3><TAG4>test4</TAG4>');
		//header("Content-type: application/x-javascript");
		
		//match all what is inside the xml tags.
		//$keywords = preg_split("/(<\/?[a-zA-Z0-9_]*>)/", $xml,-1, PREG_SPLIT_NO_EMPTY);		
		$keywords = preg_split("/(<\/?[a-zA-Z0-9_]*>)/", $xml,-1, PREG_SPLIT_NO_EMPTY);	
		
		$keywords2 =str_replace(array('<','>','/'),array('','|','|'),$xml);
		//$keywords2 =str_replace('||','|',$keywords2);
		$keywords2 = explode('|',$keywords2);
		
	}
    
    /**
     * not implemented but looking to implement it in a near future
     *
     * @param string $xml
     * @param string $tag
     * @return string
     */
    function inXML($xml,$tag)
    {
        preg_match_all("/<$tag>([a-zA-Z0-9_]+)<\/$tag>+/",$xml,$out, PREG_PATTERN_ORDER  );
        if(isset($out[1][0])) {
        	return $out[1][0];
        }
    }
    
    /**
     * not implemented but looking to implement it in a near future
     *
     * @param string $xml
     * @param string $tag
     * @param mixed $new_value
     * @return string
     */
    function changeXML($xml,$tag,$new_value)
    {
        $out = preg_replace("/(<$tag>)([a-zA-Z0-9_]+)(<\/$tag>)/","$1(!replacement!)$3",$xml);
        $out = str_replace("(!replacement!)",$new_value,$out);
        return $out;
    }
    
	/**
	 * xml outputer, allows the interaction with xml
	 *
	 * @param xml $xml
	 * @return string
	 */
	function xml($xml)
	{
		if(!$xml) return false;
		$function = '';
		if(strpos($xml,'<do>')===false) {
			$trace = debug_backtrace();
			$function = "{$trace[1]['function']}";
			if($function=='wait') {
				self::$wait = $xml;
				return true;
			}
			$function = "<do>{$function}</do>";
			if(self::$wait) {
				$function = "{$function}".self::$wait;
				self::$wait = '';
			}
		} else {
			if(self::$wait) {
				$function = "{$function}".self::$wait;
				self::$wait = '';
			}
		}
		$data = "{$function}{$xml}";
		if($function=='<do>AddEventTo</do>') {
			self::cache("<cjax>$data</cjax>",'actions');
		} else {
			self::$cjax_data_counter++;
			self::cache("<cjax>$data</cjax>");
		}
		return $data;
	}
	

	/**
	 * Used for loading "fly" events
	 *
	 * @param string $add
	 */
	function cache($add=null,$cache_id = null)
	{
		if(!self::$_is_shutdown_called) {
			$bol = register_shutdown_function(array('CoreEvents','saveSCache'));
			self::$_is_shutdown_called = true;
			self::$use_cache = true;
			flush();
			
			
		}
		if(self::onTheFly()) {
			if($cache_id) {
				if($cache_id=='actions') {
					self::$actions[] = $add;
				} else {
					self::$cache[$cache_id] = $add;
				}
			} else {
				self::$cache[self::$cjax_data_counter] = $add;
			}
		} else {
			if($cache_id) {
				self::$cache[$cache_id] = $add;
			} else {
				self::$out[self::$cjax_data_counter] = $add;
			}
		}
	}
	
	
	function removeCache($count) 
	{
		if(!self::$cache) {
			return true;
		}
		 
		if($count==count(array_keys(self::$cache))) {
			//die(print_r(self::$cache,1));
			self::$cache = array();
			if(self::$cjax_data_counter > 0) {
				self::$cjax_data_counter--;
			}
		} else if($count < self::$cjax_data_counter) {
			//die('gets fisrt');
			foreach(self::$cache as $k => $v) {
				if(!$count) {
					break;
				}
				$max = max(array_flip(self::$cache));
				unset(self::$cache[$max]);
				$count--;
			}
		}
		
	
	/*	for($i = self::$cjax_data_counter; $i < self::$cjax_data_counter-$count+1; $i --) {
			if(isset(self::$cache[$i])) {
				unset(self::$cache[$i]);
			}
		}*/

	}
	
	/**
	 * Saves the cache
	 *
	 * @return string
	 */
	function saveSCache()
	{
		if(!self::onTheFly()) {
			print ("<xml class='cjax'>".implode(self::$out)."</xml>");
			return;
		}
		if(isset(self::$cache_type) && strlen(self::$cache_type) >0) {
			$cache = self::$cache_type;
		} else {
			$cache = 'cjax_cache';
		}
		if(!self::$cache && !self::$actions) {
			self::clearCache();
			return;
		}
		if(!self::$cache) {
			self::$cache = self::$actions;
		} else {
			if(self::$actions) {
				self::$cache = array_merge(self::$cache,self::$actions);
			}
		}
		
		$out = "
		CJAX.source=\"".implode(self::$cache)."\";
		CJAX.replace_txt(true);";
		$out = str_replace(array("\n","\t"), "", $out);
		@session_start();
		GLOBAL $_SESSION;
		
		if(isset($_SESSION['cache_type'])) {
			$cache = $_SESSION['cache_type'];
		} elseif (isset($_COOKIE['cache_type'])) {
			$cache = $_COOKIE['cache_type'];
		}
		$_SESSION['cjax_cache'] = $out;
		@setcookie ('cjax_cache',$out,false);
		
		//if(self::file_write(trim($out)))
		return true;
	}
	
	function params($source,$tag)
	{
		preg_match_all("/<$tag>([a-zA-Z0-9_]+)<\/$tag>/",$source,$matches);
		return $matches;
	}
	
	function cacheType($type='cjax_cache')
	{
		self::$cache_type = $type;
	}
	
	/**
	 * write to a file in file system, used as an alrernative to for cache
	 *
	 * @param string $content
	 * @param string $flag
	 */
 	function file_write($content,$flag='w') {
 		$dir = str_replace('classes','js',__file__);
 		$dir = str_replace('core.class.php','cjax.js.php',$dir);
 		$filename = $dir;
        if (file_exists($filename)) {
            if (!is_writable($filename)) {
                if (!chmod($filename, 0666)) {
                     echo "CJAX: Error! file ($filename) is not writable, Not enough permission";
                     exit;
                };
            }
        }
        if (!$fp = @fopen($filename, $flag)) {
			echo "CJAX: Error! file ($filename) is not writable, Not enough permission";
 			exit;
        }
        if (fwrite($fp, $content) === FALSE) {
			echo "Cannot write to file ($filename)";
			exit;
        }
        if (!fclose($fp)) {
            echo "Cannot close file ($filename)";
            exit;
        }
    }
    
    function OS_SLASH()
    {
		$pos = strpos(PHP_OS, 'WIN');	    
		if ($pos !== false) {
    		return '\\';
    	}
    	return '/';
    }
	// error handler function
	/**
	 * Yet to implement
	 *
	 * @param string $errno
	 * @param string $errstr
	 * @param string $errfile
	 * @param string $errline
	 * @return string
	 */
	function CJAXErrorHandler($errno, $errstr, $errfile, $errline)
	{
	    switch ($errno) {
	    case E_USER_ERROR:
	        echo "<b>CJAX:</b> [$errno] $errstr<br />\n";
	        echo "  Fatal error on line $errline in file $errfile";
	        echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
	        echo "Aborting...<br />\n";
	        exit(1);
	        break;
	
	    case E_USER_WARNING:
	        echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
	        break;
	
	    case E_USER_NOTICE:
	        echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
	        break;
	
	    default:
	        echo "Unknown error type: [$errno] $errstr<br />\n";
	        break;
	    }
	
	    /* Don't execute PHP internal error handler */
	    return true;
	}
	
	function clearCache()
	{
		@session_start();
		$_SESSION['cjax_cache'] = '';
		@setcookie('cjax_cache','',false);
	}
	
	/**
	 * Image directory where loading images are loaded from
	 *
	 * @param unknown_type $dir
	 */
	function image_dir($dir = '')
	{
		$this->image_dir = $dir;
	}

	/**
	 * Optional text, replaces the "loading.." text when an ajax call is placed
	 *
	 * @param unknown_type $ms
	 */
	function text($ms = '')
	{
		$this->text = $ms;
	}
	
	/**
	 * Execution Event, use to create new AJAX calls in inderent scenarios
	 * for example, changing this to "onChage" will cause any element to execute 
	 * the ajax call listening the onChange event
	 * @param unknown_type $JSevent
	 */
	function JSevent($JSevent = "onclick")
	{
		$this->JSevent = $JSevent;
	}
	
	/**
	 * Simple debug option to alert any output by AJAX calls
	 *
	 * @param boolean $debug
	 */
	function debug($debug = false)
	{
		$this->debug = $debug;
	}

	/**
	 * yet to implement
	 *
	 * @param unknown_type $style
	 */
	function style($style = '')
	{
		$this->style = $style;
	}
	
	/**
	 * Require to be set to true before using a text link to execute an AJAX call
	 *
	 * @param boolean $link
	 * @return string
	 */
	function link($link = false)
	{
		$this->link = $link;
	}
	
	/**
	 * if CJAX is not located sircuntacion at the subdirectory level, and 
	 * CJAX is bein called from within a child directory then you will need to specify
	 * the url where CJAX is located (eg. http://yoursite.com/cjax)
	 *
	 * @param string $Path [CJAX URL] 
	 */
	function path($path)
	{
		self::$path = $path;
	}
	
	public function remotePath()
	{
		$host = $_SERVER['HTTP_HOST'];
		$sname = dirname($_SERVER["SCRIPT_NAME"]);
		return 'http://'.$host.$sname.'/cjax';
	}
	
	function getFile($file=null)
	{
		return self::connect($_SERVER['HTTP_HOST'],(isset($_SERVER['SERVER_PORT'])? $_SERVER['SERVER_PORT']:80),$file,true);
	}
	
	function connect($host=null,$file=null,$local = false)
	{
		$CJAX = CJAX::getInstance();
		$port = $CJAX->port;
		if(!$port) {
			$port = 80;
		}
		$fp = @fsockopen($host,$port,$errno,$errstr);
		if($fp) {
			$base = null;
			if($local) {
				if(isset($_SERVER["SCRIPT_NAME"])) {
					$base =dirname($_SERVER["SCRIPT_NAME"]);
					if(strpos($base,"/",strlen($base)-1)!="/") {
						$base.= "/";
					}
				} else {
					$base = "/";
				}
			} else  {
				$base = "/";
			}
	        @fputs($fp, "GET $base$file HTTP/1.1\r\n");
	        @fputs($fp, "HOST: $host\r\n");
	        @fputs($fp, "Connection: close\r\n\r\n");
			
		} else {
			return false;
		}
		$info = '';
		$get_info = false;
		while (!@feof($fp)) {
			if ($get_info) {
					$info .= @fread($fp, 1024);
			} else {
				if (@fgets($fp, 1024) == "\r\n") {
					$get_info = true;
				}
			}
		}
		fclose ( $fp );
		return $info;
	}
	
	
	/*
	 * checks for imputs and return values sent throught the $_GET method
	 */
	function get($value=null)
	{
		global $HTTP_POST_VARS, $HTTP_GET_VARS;
		if($value===null) $value= 'cjax';
		$v = isset($_GET[$value])? $_GET[$value] : @$_REQUEST[$value];
		if(!$value) {
			return($v)?1:0;
		}
		if(is_array($v)) {
			foreach($v as $k => $kv ) {
				if(!is_array($kv)) {
					$return[$k] =  addslashes($kv);
				} else {
					foreach($kv as $k_level => $v_level2) {
						$return[$k][$k_level] = $v_level2;
					}
				}
			}
			return $return;
		}
		return addslashes($v);
	}

	/*
	 * Checks weather or not the actual load method is being iniciated from the "load"
	 * or from an AJAX call
	 */
	function onTheFly()
	{
		if(strlen(self::get()) > 10 && is_numeric(self::get())) {
		      return false;
		}
		return true;
	}
		
	/**
	 * Syntax hilighting a program source file. It calls enscript(1) to parse and
	 * insert HTML tags to produce syntax hilighted version of the source.
	 *
	 * @param  $filename The filename of the source file to be transformed.
	 * @return A text string containing syntax hilighting version of the source,
	 *         in HTML.
	 */
	function syntax_hilight($filename) {
	    if ((substr($filename, -4) == '.php')) {
	        ob_start();
	        show_source($filename);
	        $buffer = ob_get_contents();
	        ob_end_clean();
	    } else {
	        $argv = '-q -p - -E --language=html --color '.escapeshellcmd($filename);
	        $buffer = array();
	
	        exec("enscript $argv", $buffer);
	
	        $buffer = join("\n", $buffer);
	        $buffer = eregi_replace('^.*<PRE>',  '<pre>',  $buffer);
	        $buffer = eregi_replace('</PRE>.*$', '</pre>', $buffer);
	    }
	
	    // Making it XHTML compatible.
	    $buffer = eregi_replace('<FONT COLOR="', '<span style="color:', $buffer);
	    $buffer = eregi_replace('</FONT>', '</style>', $buffer);
	
	    return $buffer;
	}
	
	/**
	 * get an instance of CJAX
	 *
	 * @return CJAX_FRAMEWORK
	 */
	function getInstance()
	{
		return singleton::getInstance('CJAX_FRAMEWORK');
	}
	
}